#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""PDF处理服务"""

import os
import re
import logging
from typing import List, Dict, Any, Optional, Tuple
from datetime import datetime

import pdfplumber

from app.core.config import settings
from app.models.pdf_models import PDFMetadata, TableData
from app.utils.pdf_utils import clean_table_data, detect_table_structure

# 创建日志记录器
logger = logging.getLogger(__name__)

class PDFService:
    """PDF处理服务"""
    
    def __init__(self):
        """初始化PDF服务"""
        pass
    
    def extract_tables_from_pdf(self, pdf_path: str, detect_titles: bool = True, merge_tables: bool = True) -> Dict[str, Any]:
        """从PDF文件中提取表格数据
        
        Args:
            pdf_path: PDF文件路径
            detect_titles: 是否检测表格标题
            merge_tables: 是否合并跨页表格
            
        Returns:
            包含元数据和表格数据的字典
        """
        logger.info(f"开始从PDF提取表格: {pdf_path}")
        
        try:
            with pdfplumber.open(pdf_path) as pdf:
                # 提取PDF元数据
                metadata = self._extract_pdf_metadata(pdf, os.path.basename(pdf_path))
                
                # 提取表格数据
                tables_with_info = self._extract_tables_with_info(pdf, detect_titles)
                
                # 如果需要，合并跨页表格
                if merge_tables:
                    tables_with_info = self._merge_continued_tables(tables_with_info)
                
                # 转换为TableData模型
                table_data_list = []
                for i, (table_info, table) in enumerate(tables_with_info):
                    table_data = TableData(
                        id=i + 1,
                        title=table_info.get("title"),
                        page=table_info.get("page"),
                        is_continued=table_info.get("is_continued", False),
                        continued_from=table_info.get("continued_from"),
                        rows=len(table),
                        cols=len(table[0]) if table and len(table) > 0 else 0,
                        headers=table[0] if table and len(table) > 0 else [],
                        data=table,
                        raw_data=table,
                        structured_data=detect_table_structure(table) if table else None
                    )
                    table_data_list.append(table_data)
                
                result = {
                    "metadata": metadata,
                    "tables": table_data_list
                }
                
                logger.info(f"PDF表格提取完成: {pdf_path}, 共提取{len(table_data_list)}个表格")
                return result
                
        except Exception as e:
            logger.error(f"PDF表格提取错误: {str(e)}")
            raise
    
    def analyze_pdf_structure(self, pdf_path: str) -> Dict[str, Any]:
        """分析PDF文件结构
        
        Args:
            pdf_path: PDF文件路径
            
        Returns:
            包含PDF结构信息的字典
        """
        logger.info(f"开始分析PDF结构: {pdf_path}")
        
        try:
            with pdfplumber.open(pdf_path) as pdf:
                # 提取PDF元数据
                metadata = self._extract_pdf_metadata(pdf, os.path.basename(pdf_path))
                
                # 分析页面结构
                pages_info = []
                for i, page in enumerate(pdf.pages):
                    # 提取页面文本
                    text = page.extract_text()
                    words = page.extract_words()
                    
                    # 提取页面表格
                    tables = page.extract_tables()
                    
                    # 提取页面图像
                    images = page.images
                    
                    # 构建页面信息
                    page_info = {
                        "page_number": i + 1,
                        "width": page.width,
                        "height": page.height,
                        "text_length": len(text) if text else 0,
                        "word_count": len(words),
                        "table_count": len(tables),
                        "image_count": len(images) if images else 0,
                    }
                    pages_info.append(page_info)
                
                # 构建结构分析结果
                result = {
                    "metadata": metadata,
                    "page_count": len(pdf.pages),
                    "pages": pages_info,
                    "has_tables": any(page["table_count"] > 0 for page in pages_info),
                    "has_images": any(page["image_count"] > 0 for page in pages_info),
                }
                
                logger.info(f"PDF结构分析完成: {pdf_path}")
                return result
                
        except Exception as e:
            logger.error(f"PDF结构分析错误: {str(e)}")
            raise
    
    def _extract_pdf_metadata(self, pdf, filename: str) -> PDFMetadata:
        """提取PDF元数据
        
        Args:
            pdf: pdfplumber PDF对象
            filename: 文件名
            
        Returns:
            PDFMetadata对象
        """
        # 获取PDF元数据
        meta = pdf.metadata
        
        # 解析创建和修改时间
        created_at = None
        modified_at = None
        
        if meta and "CreationDate" in meta:
            try:
                # 尝试解析PDF日期格式 (D:YYYYMMDDHHmmSS)
                date_str = meta["CreationDate"]
                if date_str and date_str.startswith("D:"):
                    date_str = date_str[2:]
                    created_at = datetime.strptime(date_str[:14], "%Y%m%d%H%M%S")
            except Exception as e:
                logger.warning(f"无法解析PDF创建日期: {str(e)}")
        
        if meta and "ModDate" in meta:
            try:
                date_str = meta["ModDate"]
                if date_str and date_str.startswith("D:"):
                    date_str = date_str[2:]
                    modified_at = datetime.strptime(date_str[:14], "%Y%m%d%H%M%S")
            except Exception as e:
                logger.warning(f"无法解析PDF修改日期: {str(e)}")
        
        # 提取关键词
        keywords = []
        if meta and "Keywords" in meta and meta["Keywords"]:
            keywords = [k.strip() for k in meta["Keywords"].split(",")]
        
        # 构建元数据对象
        metadata = PDFMetadata(
            filename=filename,
            page_count=len(pdf.pages),
            created_at=created_at,
            modified_at=modified_at,
            author=meta.get("Author") if meta else None,
            title=meta.get("Title") if meta else None,
            subject=meta.get("Subject") if meta else None,
            keywords=keywords,
            producer=meta.get("Producer") if meta else None,
            creator=meta.get("Creator") if meta else None
        )
        
        return metadata
    
    def _extract_tables_with_info(self, pdf, detect_titles: bool = True) -> List[Tuple[Dict[str, Any], List[List[Any]]]]:
        """提取表格及其相关信息
        
        Args:
            pdf: pdfplumber PDF对象
            detect_titles: 是否检测表格标题
            
        Returns:
            包含表格信息和表格数据的元组列表
        """
        tables_with_info = []
        
        for page_num, page in enumerate(pdf.pages):
            # 提取页面文本
            text = page.extract_text()
            text_lines = text.split('\n') if text else []
            
            # 提取可能的表格标题
            possible_titles = self._find_possible_table_titles(text_lines) if detect_titles else []
            
            # 提取表格
            tables = page.extract_tables()
            
            for table_idx, table in enumerate(tables):
                # 清理表格数据
                cleaned_table = clean_table_data(table)
                
                # 如果表格为空，跳过
                if not cleaned_table or len(cleaned_table) == 0:
                    continue
                
                # 查找表格标题
                title = None
                if detect_titles:
                    title = self._find_table_title(page, text_lines, cleaned_table, table_idx, possible_titles)
                
                # 表格信息
                table_info = {
                    "page": page_num + 1,
                    "title": title,
                    "is_continued": False,
                    "continued_from": None
                }
                
                tables_with_info.append((table_info, cleaned_table))
        
        return tables_with_info
    
    def _find_possible_table_titles(self, text_lines: List[str]) -> List[Tuple[int, str]]:
        """从文本中找出可能的表格标题行
        
        Args:
            text_lines: 文本行列表
            
        Returns:
            可能的表格标题行及其索引的元组列表
        """
        possible_titles = []
        
        # 表格标题通常包含"表"字或数字+标题模式
        patterns = [
            r'表\s*\d+[\s\S]*',  # 匹配"表1 xxx"、"表 2 xxx"等
            r'表\s*[一二三四五六七八九十]+[\s\S]*',  # 匹配"表一 xxx"、"表 二 xxx"等
            r'[Tt]able\s*\d+[\s\S]*',  # 匹配英文表格标题
        ]
        
        for i, line in enumerate(text_lines):
            # 检查是否匹配表格标题模式
            for pattern in patterns:
                if re.match(pattern, line):
                    possible_titles.append((i, line))
                    break
            
            # 如果行中包含"表"字且长度适中，可能是表格标题
            if '表' in line and 5 <= len(line) <= 100 and not any(p in line for p in ['表格', '表示']):
                possible_titles.append((i, line))
        
        return possible_titles
    
    def _find_table_title(self, page, text_lines: List[str], table: List[List[Any]], table_idx: int, possible_titles: List[Tuple[int, str]]) -> Optional[str]:
        """尝试找到表格的标题
        
        Args:
            page: pdfplumber页面对象
            text_lines: 文本行列表
            table: 表格数据
            table_idx: 表格索引
            possible_titles: 可能的表格标题列表
            
        Returns:
            表格标题或None
        """
        # 默认标题
        default_title = f"无标题"
        
        # 方法1: 尝试通过表格内容在文本中的位置确定标题
        title_from_content = self._find_title_by_table_content(text_lines, table)
        if title_from_content:
            return title_from_content
        
        # 方法2: 尝试通过表格位置和可能的标题列表确定
        title_from_position = self._find_title_by_position(page, table, table_idx, possible_titles)
        if title_from_position:
            return title_from_position
        
        # 如果都找不到，返回默认标题
        return default_title
    
    def _find_title_by_table_content(self, text_lines: List[str], table: List[List[Any]]) -> Optional[str]:
        """通过表格内容在文本中的位置确定标题
        
        Args:
            text_lines: 文本行列表
            table: 表格数据
            
        Returns:
            表格标题或None
        """
        if not table or not text_lines:
            return None
        
        # 尝试使用表格的前几行内容来定位
        search_rows = min(3, len(table))  # 最多检查前3行
        
        for row_idx in range(search_rows):
            row = table[row_idx]
            # 跳过空行
            if not any(cell for cell in row if cell):
                continue
                
            # 尝试找到包含这行内容的文本行
            for cell in row:
                if not cell:
                    continue
                    
                # 清理单元格文本，移除多余空格
                cell_text = str(cell).strip()
                if len(cell_text) < 3:  # 跳过太短的单元格
                    continue
                    
                # 在文本中查找这个单元格内容
                for i, line in enumerate(text_lines):
                    if cell_text in line:
                        # 找到了表格内容，检查前面的行是否可能是标题
                        for j in range(i-1, max(0, i-5), -1):  # 向上查找最多4行
                            prev_line = text_lines[j]
                            if self._is_likely_table_title(prev_line):
                                return prev_line
                        break
        
        return None
    
    def _find_title_by_position(self, page, table: List[List[Any]], table_idx: int, possible_titles: List[Tuple[int, str]]) -> Optional[str]:
        """通过表格位置和可能的标题列表确定标题
        
        Args:
            page: pdfplumber页面对象
            table: 表格数据
            table_idx: 表格索引
            possible_titles: 可能的表格标题列表
            
        Returns:
            表格标题或None
        """
        if not possible_titles:
            return None
        
        # 如果无法获取表格位置，选择最接近的可能标题
        if possible_titles:
            return possible_titles[0][1]
        
        return None
    
    def _is_likely_table_title(self, text: str) -> bool:
        """判断文本是否可能是表格标题
        
        Args:
            text: 文本
            
        Returns:
            是否可能是表格标题
        """
        # 表格标题通常较短，包含"表"字或数字
        if not text or len(text) > 100:
            return False
            
        # 检查是否包含表格标题的特征
        patterns = [
            r'[报表|明细表|信息表|]\s*\d+',  # 匹配"表1"、"表 2"等
            r'表\s*[一二三四五六七八九十]+',  # 匹配"表一"、"表 二"等
            r'[Tt]able\s*\d+',  # 匹配英文表格标题
        ]
        
        for pattern in patterns:
            if re.search(pattern, text):
                return True
        
        # 如果包含"表"字且不是常见的非标题词组
        if '表' in text and not any(p in text for p in ['表格', '表示', '代表', '表达', '填表']):
            return True
            
        return False
    
    def _merge_continued_tables(self, tables_with_info: List[Tuple[Dict[str, Any], List[List[Any]]]]) -> List[Tuple[Dict[str, Any], List[List[Any]]]]:
        """合并跨页的表格
        
        Args:
            tables_with_info: 表格信息和数据的元组列表
            
        Returns:
            合并后的表格信息和数据的元组列表
        """
        if not tables_with_info:
            return []
            
        merged_tables = []
        i = 0
        
        while i < len(tables_with_info):
            current_info, current_table = tables_with_info[i]
            
            # 检查是否是续表
            title = current_info.get("title", "")
            is_continued = title and ("续" in title or "(续)" in title or "（续）" in title)
            
            if is_continued and merged_tables:
                # 这是一个续表，合并到前一个表格
                prev_info, prev_table = merged_tables[-1]
                # 跳过表头（如果与前一个表格相同）
                if len(current_table) > 1 and len(prev_table) > 0 and self._table_headers_similar(current_table[0], prev_table[0]):
                    merged_table = prev_table + current_table[1:]
                else:
                    merged_table = prev_table + current_table
                
                # 更新表格信息
                merged_tables[-1] = (prev_info, merged_table)
            else:
                # 检查下一个表格是否可能是当前表格的续表（通过结构比较）
                if i + 1 < len(tables_with_info):
                    next_info, next_table = tables_with_info[i + 1]
                    next_title = next_info.get("title", "")
                    
                    if self._tables_likely_continued(current_table, next_table) and not self._is_new_table_title(next_title):
                        # 可能是同一个表格被分页了，但没有明确标记为续表
                        # 合并表格，并继续检查后续表格
                        combined_table = current_table
                        j = i + 1
                        
                        while j < len(tables_with_info) and self._tables_likely_continued(combined_table, tables_with_info[j][1]):
                            _, next_part = tables_with_info[j]
                            # 跳过表头（如果与前一个表格相同）
                            if len(next_part) > 1 and self._table_headers_similar(next_part[0], combined_table[0]):
                                combined_table = combined_table + next_part[1:]
                            else:
                                combined_table = combined_table + next_part
                            j += 1
                        
                        # 更新表格信息
                        merged_tables.append((current_info, combined_table))
                        i = j - 1  # 跳过已合并的表格
                    else:
                        # 这是一个新表格
                        merged_tables.append((current_info, current_table))
                else:
                    # 这是最后一个表格
                    merged_tables.append((current_info, current_table))
            
            i += 1
        
        # 更新续表信息
        for i in range(1, len(merged_tables)):
            info, _ = merged_tables[i]
            title = info.get("title", "")
            
            if "续" in title or "(续)" in title or "（续）" in title:
                info["is_continued"] = True
                info["continued_from"] = i - 1
        
        return merged_tables
    
    def _table_headers_similar(self, header1: List[Any], header2: List[Any]) -> bool:
        """判断两个表头是否相似
        
        Args:
            header1: 第一个表头
            header2: 第二个表头
            
        Returns:
            是否相似
        """
        if not header1 or not header2 or len(header1) != len(header2):
            return False
            
        # 计算相同单元格的比例
        same_cells = sum(1 for a, b in zip(header1, header2) if str(a).strip() == str(b).strip())
        similarity = same_cells / len(header1) if len(header1) > 0 else 0
        
        return similarity > 0.7  # 如果70%以上的单元格相同，认为表头相似
    
    def _tables_likely_continued(self, table1: List[List[Any]], table2: List[List[Any]]) -> bool:
        """判断table2是否可能是table1的续表
        
        Args:
            table1: 第一个表格
            table2: 第二个表格
            
        Returns:
            是否可能是续表
        """
        if not table1 or not table2:
            return False
            
        # 检查列数是否相同
        if len(table1[0]) != len(table2[0]):
            return False
            
        # 检查表头是否相似
        return self._table_headers_similar(table1[0], table2[0])
    
    def _is_new_table_title(self, title: str) -> bool:
        """判断标题是否明确表示这是一个新表格
        
        Args:
            title: 表格标题
            
        Returns:
            是否是新表格标题
        """
        if not title:
            return False
        if title == '无标题':
            return False
            
        # 如果标题包含表号，可能是新表格
        patterns = [
            r'[报表|明细表|信息表|]\s*',
            r'表\s*[一二三四五六七八九十]+',
            r'[Tt]able\s*\d+',
        ]
        
        for pattern in patterns:
            if re.search(pattern, title):
                return True
                
        return False